package com.gitee.l0km.codegen.base;

import java.lang.reflect.Type;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import com.gitee.l0km.com4j.base.Assert;
import com.gitee.l0km.com4j.base.ClassLoaderUtils;
import com.gitee.l0km.com4j.base.Judge;
import com.gitee.l0km.com4j.basex.reflection.generics.TypeVisitor;
import com.google.common.base.MoreObjects;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.isNullOrEmpty;

public class CodeGenUtils {
	/*
	 * 生成代码时,需要导入的类的ClassLoader，由插件提供
	 */
	private static URLClassLoader projectClassLoader ;

	public static final <T> Set<T> toSet(T[] a) {
		return new HashSet<T>(Arrays.asList(a));
	}

	public static final <T> Set<T> toSetIfnotDup(T[] a) {
		Set<T> set = new HashSet<T>();
		for (T o : a) {
			if (set.contains(o))
				return null;
			set.add(o);
		}
		return set;
	}

	public static final <T> boolean hasDuplicative(T[] a) {
		return toSetIfnotDup(a) == null;
	}

	public static final <T> Set<T> toSet(Collection<T> c) {
		Set<T> set = new HashSet<T>();
		for (T o : c) {
			set.add(o);
		}
		return set;
	}

	public static final <T> Set<T> toSetIfnotDup(Collection<T> c) {
		Set<T> set = new HashSet<T>();
		for (T o : c) {
			if (set.contains(o))
				return null;
			set.add(o);
		}
		return set;
	}

	public static final <T> boolean hasDuplicative(Collection<T> c) {
		return toSetIfnotDup(c) != null;
	}

	/**
	 *  
	 * 允许{@code values}有重复的值
	 * 
	 * @param keys
	 * @param values
	 * @throws IllegalArgumentException
	 * @see #createMap(Object[], Object[], boolean)
	 */
	public static final <K, V> Map<K, V> createMap(K[] keys, V[] values) throws IllegalArgumentException {
		return createMap(keys, values, false);
	}

	/**
	 * 根据 {@code keys,values}提供的参数创建{@link java.util.Map}对象<br>
	 * {@code keys,values}数组长度必须一致 如果参数不能满足条件(为空/有重复的值/数组长度不一致),则抛出异常
	 * 
	 * @param keys
	 *            用作key的数组(不允许有重复的值,不允许为{@code null})
	 * @param values
	 *            用作value的数组(不允许为{@code null},{@code uniqueValues}为{@code true}时不允许有重复的值,)
	 * @param uniqueValues
	 *            是否要求values中的值都是不重复的
	 * @throws IllegalArgumentException
	 */
	public static final <K, V> Map<K, V> createMap(K[] keys, V[] values, boolean uniqueValues)
			throws IllegalArgumentException {
		Assert.notNull(keys, "keys");
		Assert.notNull(values, "values");
		if (keys.length != values.length){
			throw new IllegalArgumentException("the keys and values have different length");
		}
		if (Judge.hasNull(keys)){
			throw new IllegalArgumentException("the keys have null elements");
		}
		if (Judge.hasNull(values)){
			throw new IllegalArgumentException("the values have null elements");
		}
		if (null == CodeGenUtils.toSetIfnotDup(keys)){
			throw new IllegalArgumentException("the keys have duplicated elements");
		}
		if (uniqueValues && null == CodeGenUtils.toSetIfnotDup(values)){
			throw new IllegalArgumentException("the values have duplicated elements");
		}
		Map<K, V> map = new LinkedHashMap<K, V>(keys.length << 1);
		for (int i = 0; i < keys.length; i++){
			map.put(keys[i], values[i]);
		}
		return map;
	}

	public static final String firstUpperCase(String name) {
		StringBuffer buffer = new StringBuffer();
		Matcher match = Pattern.compile("^([a-z])(\\w*)").matcher(name);
		if (match.find())
			match.appendReplacement(buffer, match.group(1).toUpperCase() + match.group(2));
		match.appendTail(buffer);
		return buffer.toString();
	}

	public static final void addImportedTypes(Map<String, Class<?>> importedList, Type... types) {
		for (Type type : types) {
			addImportedClass(importedList, type);
		}
	}

	public static final void addImportedTypes(Map<String, Class<?>> importedList, String... types)
			throws ClassNotFoundException {
		for (String type : types) {
			addImportedClass(importedList, CodeGenUtils.forName(type));
		}
	}
	public static final void addImportedTypes(Map<String, Class<?>> importedList, Collection<Type> types) {
		for (Type type : types) {
			addImportedClass(importedList, type);
		}
	}
	public static final void addImportedTypes(Map<String, Class<?>> importedList, Iterable<Type> types) {
		for (Type type : types) {
			addImportedClass(importedList, type);
		}
	}

	public static final boolean isJavaBuildinClass(Class<?> clazz) {
		return (clazz.isPrimitive() || CodeGenUtils.isJavaLangClass(clazz));
	}
	public static final Map<String, Class<?>> addImportedClass(Map<String, Class<?>> importedList, Type type) {
		final Map<String, Class<?>> _importedList = MoreObjects.firstNonNull(importedList, new HashMap<String, Class<?>>());
		new TypeVisitor() {

			@Override
			protected void visitClass(Class<?> t) {
				if(t.isArray()) {
					visitClass(t.getComponentType());
				}else {
					if (!isJavaBuildinClass(t)){
						_importedList.putIfAbsent(t.getSimpleName(), t);
					}
				}
			}
			
		}.visit(type);
		return _importedList;
	}

	public static final Class<?> getElementClass(Class<?> clazz) {
		return clazz.isArray() ? getElementClass(clazz.getComponentType()) : clazz;
	}

	public static final boolean isJavaLangClass(Class<?> clazz) {
		return null == clazz.getPackage() ? false : "java.lang".equals(clazz.getPackage().getName());
	}

	/**
	 * 判断一个类是否为基本数据类型。
	 * 
	 * @param clazz
	 *            要判断的类。
	 * @return true 表示为基本数据类型。
	 */
	public static boolean isBaseDataType(Class<?> clazz) {
		return (clazz == String.class || PRIMITIVE_TYPE_MAP.values().contains(clazz) || clazz.isPrimitive());
	}

	private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_MAP = new HashMap<Class<?>, Class<?>>() {
		private static final long serialVersionUID = 6156824678779843289L;
		{
			put(int.class, Integer.class);
			put(long.class, Long.class);
			put(byte.class, Byte.class);
			put(double.class, Double.class);
			put(float.class, Float.class);
			put(char.class, Character.class);
			put(short.class, Short.class);
			put(boolean.class, Boolean.class);
		}
	};

	/**
	 * 返回primitive类型对应的Object类型
	 * @param primitive
	 */
	public static final Class<?> getTypeForPrimitive(Class<?> primitive) {
		Assert.notNull(primitive, "primitive");
		if (!primitive.isPrimitive()){
			throw new IllegalArgumentException(String.format("%s not a primitive type", primitive.getName()));
		}
		return PRIMITIVE_TYPE_MAP.get(primitive);
	}

	public static final Comparator<Class<?>> CLASS_COMPARATOR = new Comparator<Class<?>>() {
		@Override
		public int compare(Class<?> o1, Class<?> o2) {
			return o1.getName().compareTo(o2.getName());
		}
	};

	public static final ArrayList<Class<?>> sortClass(Collection<Class<?>> collection) {
		ArrayList<Class<?>> classList = new ArrayList<Class<?>>(collection);
		Collections.sort(classList, CLASS_COMPARATOR);
		return classList;
	}

	public static final Class<?>[] sortClass(Class<?>[] array) {
		Class<?>[] newArray = Arrays.copyOf(array, array.length);
		Arrays.sort(newArray, CLASS_COMPARATOR);
		return newArray;
	}

	/**
	 * 将namespace转换为包名
	 * @param namespace
	 */
	public static String packageFromNamespace(String namespace){
		Assert.notEmpty(namespace, "namespace");
		// 正则表达式提取namespace中的包结构
		List<String> names = Arrays.asList(namespace.replaceAll("http://((?:(?:[\\w\\d]+)\\.?)+)(?:/(?:[\\d\\w]+))?", "$1").split("\\."));
		Collections.reverse(names);
		return StringUtils.join(names.iterator(), ".");
	}

	/**
	 * 根据namespace将className转换为Class,如果失败则抛出{@link RuntimeException}
	 * @param namespace
	 * @param className
	 */
	public static Class<?> classFromNamespaceAndClassName(String namespace,String className){
		Assert.notEmpty(className, "className");
		try {
			return CodeGenUtils.forName(packageFromNamespace(namespace)+"."+className);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}		
	}

	/**
	 * 在map中通过V反向查找Key
	 * @param map map中的V也必须是不重复的
	 * @param value
	 */
	public static final <K,V>K getKey(Map<K,V> map,V value){
		Assert.notNull(map, "map");
		Assert.notNull(value,"value");
		for(Entry<K, V> e: map.entrySet()){
			if(e.getValue().equals(value)){
				return e.getKey();
			}
		}
		return null;
	}
	/**
	 * 使用 {@link #projectClassLoader}返回 {@code classname} 指定类名的Class对象
	 * 
	 * @since 3.3.0
	 */
	public static Class<?> forName(String classname) throws ClassNotFoundException {
		return Class.forName(classname, false, getProjectClassLoader());
	}

	/**
	 * 根据输入的classpath初始化{@link #projectClassLoader}字段
	 * @param parent 父级类加载器，为{@code null}使用当前类的{@link ClassLoader}
	 * @since 3.3.0
	 */
	public static void initProjectClassLoader(String classpath, ClassLoader parent) {
		checkArgument(!isNullOrEmpty(classpath),"classpath is null or empty");
		projectClassLoader = ClassLoaderUtils.buildClassLoader(classpath, 
				checkNotNull(parent, "parent is null"),
				false);
	}
	/**
	 * 返回由classpath创建的当前项目的{@link URLClassLoader}实例
	 * @since 3.3.0
	 */
	public static URLClassLoader getProjectClassLoader() {
		checkState(null != projectClassLoader, "projectClassLoader is uninitialized");
		return projectClassLoader;
	}
}
